1 using System;
2 using System.Collections.Generic;
3 using System.IO;
4 using UnityEngine;
5
6 public class GhostMove : MonoBehaviour {
7
8 // ----------------------------
9 // Navigation variables
10 private Vector3 waypoint; // AI-determined waypoint
11 private Queue<Vector3> waypoints; // waypoints used on Init and Scatter states
12
13 // direction is set from the AI component
14 public Vector3 _direction;
15 public Vector3 direction
16 {
17 get
18 {
19 return _direction;
20 }
21
22 set
23 {
24 _direction = value;
25 Vector3 pos = new Vector3((int)transform.position.x, (int)transform.position.y, (int)transform.position.z);
26 waypoint = pos + _direction;
27 //Debug.Log ("waypoint (" + waypoint.position.x + ", " + waypoint.position.y + ") set! _direction: " + _direction.x + ", " + _direction.y);
28
29 }
30 }
31
32 public float speed = 0.3f;
33
34 // ----------------------------
35 // Ghost mode variables
36 public float scatterLength = 5f;
37 public float waitLength = 0.0f;
38
39 private float timeToEndScatter;
40 private float timeToEndWait;
41
42 enum State { Wait, Init, Scatter, Chase, Run };
43 State state;
44
45 private Vector3 _startPos;
46 private float _timeToWhite;
47 private float _timeToToggleWhite;
48 private float _toggleInterval;
49 private bool isWhite = false;
50
51 // handles
52 public GameGUINavigation GUINav;
53 public PlayerController pacman;
54 private GameManager _gm;
55
56 //-----------------------------------------------------------------------------------------
57 // variables end, functions begin
58 void Start()
59 {
60 _gm = GameObject.Find("Game Manager").GetComponent<GameManager>();
61 _toggleInterval = _gm.scareLength * 0.33f * 0.20f;
62 InitializeGhost();
63 }
64
65 public float DISTANCE;
66
67 void FixedUpdate ()
68 {
69 DISTANCE = Vector3.Distance(transform.position, waypoint);
70
71 if(GameManager.gameState == GameManager.GameState.Game){
72 animate ();
73
74 switch(state)
75 {
76 case State.Wait:
77 Wait();
78 break;
79
80 case State.Init:
81 Init();
82 break;
83
84 case State.Scatter:
85 Scatter();
86 break;
87
88 case State.Chase:
89 ChaseAI();
90 break;
91
92 case State.Run:
93 RunAway();
94 break;
95 }
96 }
97 }
98
99 //-----------------------------------------------------------------------------------
100 // Start() functions
101
102 public void InitializeGhost()
103 {
104 _startPos = getStartPosAccordingToName();
105 waypoint = transform.position; // to avoid flickering animation
106 state = State.Wait;
107 timeToEndWait = Time.time + waitLength + GUINav.initialDelay;
108 InitializeWaypoints(state);
109 }
110
111 public void InitializeGhost(Vector3 pos)
112 {
113 transform.position = pos;
114 waypoint = transform.position; // to avoid flickering animation
115 state = State.Wait;
116 timeToEndWait = Time.time + waitLength + GUINav.initialDelay;
117 InitializeWaypoints(state);
118 }
119
120
121 private void InitializeWaypoints(State st)
122 {
123 //--------------------------------------------------
124 // File Format: Init and Scatter coordinates separated by empty line
125 // Init X,Y
126 // Init X,Y
127 //
128 // Scatter X,Y
129 // Scatter X,Y
130
131 //--------------------------------------------------
132 // hardcode waypoints according to name.
133 string data = "";
134 switch (name)
135 {
136 case "blinky":
137 data = @"22 20
138 22 26
139
140 27 26
141 27 30
142 22 30
143 22 26";
144 break;
145 case "pinky":
146 data = @"14.5 17
147 14 17
148 14 20
149 7 20
150
151 7 26
152 7 30
153 2 30
154 2 26";
155 break;
156 case "inky":
157 data = @"16.5 17
158 15 17
159 15 20
160 22 20
161
162 22 8
163 19 8
164 19 5
165 16 5
166 16 2
167 27 2
168 27 5
169 22 5";
170 break;
171 case "clyde":
172 data = @"12.5 17
173 14 17
174 14 20
175 7 20
176
177 7 8
178 7 5
179 2 5
180 2 2
181 13 2
182 13 5
183 10 5
184 10 8";
185 break;
186
187 }
188
189 //-------------------------------------------------
190 // read from the hardcoded waypoints
191 string line;
192
193 waypoints = new Queue<Vector3>();
194 Vector3 wp;
195
196 if (st == State.Init)
197 {
198 using (StringReader reader = new StringReader(data))
199 {
200 // stop reading if empty line is reached
201 while ((line = reader.ReadLine()) != null)
202 {
203 if (line.Length == 0) break; // DOES IT WORK????
204
205 string[] values = line.Split(' ');
206 float x = float.Parse(values[0]);
207 float y = float.Parse(values[1]);
208
209 wp = new Vector3(x, y, 0);
210 waypoints.Enqueue(wp);
211 }
212 }
213 }
214
215 if (st == State.Scatter)
216 {
217 // skip until empty line is reached, read coordinates afterwards
218 bool scatterWps = false; // Scatter waypoints
219
220 using (StringReader reader = new StringReader(data))
221 {
222 while ((line = reader.ReadLine()) != null)
223 {
224 if (line.Length == 0)
225 {
226 scatterWps = true; // we reached the scatter waypoints
227 continue; // do not read empty line, go to next line
228 }
229
230 if (scatterWps)
231 {
232 string[] values = line.Split(' ');
233 int x = Int32.Parse(values[0]);
234 int y = Int32.Parse(values[1]);
235
236 wp = new Vector3(x, y, 0);
237 waypoints.Enqueue(wp);
238 }
239 }
240 }
241 }
242
243 // if in wait state, patrol vertically
244 if (st == State.Wait)
245 {
246 Vector3 pos = transform.position;
247
248 // inky and clyde start going down and then up
249 if (transform.name == "inky" || transform.name == "clyde")
250 {
251 waypoints.Enqueue(new Vector3(pos.x, pos.y - 0.5f, 0f));
252 waypoints.Enqueue(new Vector3(pos.x, pos.y + 0.5f, 0f));
253 }
254 // while pinky start going up and then down
255 else
256 {
257 waypoints.Enqueue(new Vector3(pos.x, pos.y + 0.5f, 0f));
258 waypoints.Enqueue(new Vector3(pos.x, pos.y - 0.5f, 0f));
259 }
260 }
261
262 }
263
264 private Vector3 getStartPosAccordingToName()
265 {
266 switch (gameObject.name)
267 {
268 case "blinky":
269 return new Vector3(15f, 20f, 0f);
270
271 case "pinky":
272 return new Vector3(14.5f, 17f, 0f);
273
274 case "inky":
275 return new Vector3(16.5f, 17f, 0f);
276
277 case "clyde":
278 return new Vector3(12.5f, 17f, 0f);
279 }
280
281 return new Vector3();
282 }
283
284 //------------------------------------------------------------------------------------
285 // Update functions
286 void animate()
287 {
288 Vector3 dir = waypoint - transform.position;
289 GetComponent<Animator>().SetFloat("DirX", dir.x);
290 GetComponent<Animator>().SetFloat("DirY", dir.y);
291 GetComponent<Animator>().SetBool("Run", false);
292 }
293
294 void OnTriggerEnter2D(Collider2D other)
295 {
296 if(other.name == "pacman")
297 {
298 //Destroy(other.gameObject);
299 if (state == State.Run)
300 {
301 Calm();
302 InitializeGhost(_startPos);
303 pacman.UpdateScore();
304 }
305
306 else
307 {
308 _gm.LoseLife();
309 }
310
311 }
312 }
313
314 //-----------------------------------------------------------------------------------
315 // State functions
316 void Wait()
317 {
318 if(Time.time >= timeToEndWait)
319 {
320 state = State.Init;
321 waypoints.Clear();
322 InitializeWaypoints(state);
323 }
324
325 // get the next waypoint and move towards it
326 MoveToWaypoint(true);
327 }
328
329 void Init()
330 {
331 _timeToWhite = 0;
332
333 // if the Queue is cleared, do some clean up and change the state
334 if(waypoints.Count == 0)
335 {
336 state = State.Scatter;
337
338 //get direction according to sprite name
339 string name = GetComponent<SpriteRenderer>().sprite.name;
340 if(name[name.Length-1] == '0' || name[name.Length-1] == '1') direction = Vector3.right;
341 if(name[name.Length-1] == '2' || name[name.Length-1] == '3') direction = Vector3.left;
342 if(name[name.Length-1] == '4' || name[name.Length-1] == '5') direction = Vector3.up;
343 if(name[name.Length-1] == '6' || name[name.Length-1] == '7') direction = Vector3.down;
344
345 InitializeWaypoints(state);
346 timeToEndScatter = Time.time + scatterLength;
347
348 return;
349 }
350
351 // get the next waypoint and move towards it
352 MoveToWaypoint();
353 }
354
355 void Scatter()
356 {
357 if(Time.time >= timeToEndScatter)
358 {
359 waypoints.Clear();
360 state = State.Chase;
361 return;
362 }
363
364 // get the next waypoint and move towards it
365 MoveToWaypoint(true);
366
367 }
368
369 void ChaseAI()
370 {
371
372 // if not at waypoint, move towards it
373 if (Vector3.Distance(transform.position, waypoint) > 0.000000000001)
374 {
375 Vector2 p = Vector2.MoveTowards(transform.position, waypoint, speed);
376 GetComponent<Rigidbody2D>().MovePosition(p);
377 }
378
379 // if at waypoint, run AI module
380 else GetComponent<AI>().AILogic();
381
382 }
383
384 void RunAway()
385 {
386 GetComponent<Animator>().SetBool("Run", true);
387
388 if(Time.time >= _timeToWhite && Time.time >= _timeToToggleWhite) ToggleBlueWhite();
389
390 // if not at waypoint, move towards it
391 if (Vector3.Distance(transform.position, waypoint) > 0.000000000001)
392 {
393 Vector2 p = Vector2.MoveTowards(transform.position, waypoint, speed);
394 GetComponent<Rigidbody2D>().MovePosition(p);
395 }
396
397 // if at waypoint, run AI run away logic
398 else GetComponent<AI>().RunLogic();
399
400 }
401
402 //------------------------------------------------------------------------------
403 // Utility functions
404 void MoveToWaypoint(bool loop = false)
405 {
406 waypoint = waypoints.Peek(); // get the waypoint (CHECK NULL?)
407 if (Vector3.Distance(transform.position, waypoint) > 0.000000000001) // if its not reached
408 { // move towards it
409 _direction = Vector3.Normalize(waypoint - transform.position); // dont screw up waypoint by calling public setter
410 Vector2 p = Vector2.MoveTowards(transform.position, waypoint, speed);
411 GetComponent<Rigidbody2D>().MovePosition(p);
412 }
413 else // if waypoint is reached, remove it from the queue
414 {
415 if(loop) waypoints.Enqueue(waypoints.Dequeue());
416 else waypoints.Dequeue();
417 }
418 }
419
420 public void Frighten()
421 {
422 state = State.Run;
423 _direction *= -1;
424
425 _timeToWhite = Time.time + _gm.scareLength * 0.66f;
426 _timeToToggleWhite = _timeToWhite;
427 GetComponent<Animator>().SetBool("Run_White", false);
428
429 }
430
431 public void Calm()
432 {
433 // if the ghost is not running, do nothing
434 if (state != State.Run) return;
435
436 waypoints.Clear ();
437 state = State.Chase;
438 _timeToToggleWhite = 0;
439 _timeToWhite = 0;
440 GetComponent<Animator>().SetBool("Run_White", false);
441 GetComponent<Animator>().SetBool("Run", false);
442 }
443
444 public void ToggleBlueWhite()
445 {
446 isWhite = !isWhite;
447 GetComponent<Animator>().SetBool("Run_White", isWhite);
448 _timeToToggleWhite = Time.time + _toggleInterval;
449 }
450
451 }
----------------------------
Navigation variables
private Vector3 waypoint; AI-determined waypoint
private Queue waypoints; waypoints used on Init and Scatter states
direction is set from the AI component
Debug.Log ("waypoint (" + waypoint.position.x + ", " + waypoint.position.y + ") set! _direction: " + _direction.x + ", " + _direction.y);
----------------------------
Ghost mode variables
handles
-----------------------------------------------------------------------------------------
variables end, functions begin
-----------------------------------------------------------------------------------
Start() functions
waypoint = transform.position; to avoid flickering animation
waypoint = transform.position; to avoid flickering animation
--------------------------------------------------
File Format: Init and Scatter coordinates separated by empty line
Init X,Y
Init X,Y
Scatter X,Y
Scatter X,Y
--------------------------------------------------
hardcode waypoints according to name.
-------------------------------------------------
read from the hardcoded waypoints
stop reading if empty line is reached
if (line.Length == 0) break; DOES IT WORK????
skip until empty line is reached, read coordinates afterwards
bool scatterWps = false; Scatter waypoints
scatterWps = true; we reached the scatter waypoints
continue; do not read empty line, go to next line
if in wait state, patrol vertically
inky and clyde start going down and then up
while pinky start going up and then down
------------------------------------------------------------------------------------
Update functions
Destroy(other.gameObject);
-----------------------------------------------------------------------------------
State functions
get the next waypoint and move towards it
if the Queue is cleared, do some clean up and change the state
get direction according to sprite name
get the next waypoint and move towards it
get the next waypoint and move towards it
if not at waypoint, move towards it
if at waypoint, run AI module
if not at waypoint, move towards it
if at waypoint, run AI run away logic
------------------------------------------------------------------------------
Utility functions
waypoint = waypoints.Peek(); get the waypoint (CHECK NULL?)
if (Vector3.Distance(transform.position, waypoint) > 0.000000000001) if its not reached
{ move towards it
_direction = Vector3.Normalize(waypoint - transform.position); dont screw up waypoint by calling public setter
else if waypoint is reached, remove it from the queue
if the ghost is not running, do nothing